package cn.test;

/**
 * @author bigBear
 * @version 1.0
 * @date 2024-09-26 10:51
 */

import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import static java.lang.Math.*;

//@SpringBootTest
public class Beginner {

    /**
     * 2235. 两整数相加
     * 给你两个整数 num1 和 num2，返回这两个整数的和。
     *
     * @param num1
     * @param num2
     * @return
     */
    public int sum(int num1, int num2) {
        return num1 + num2;
    }

    @Test
    public void test2235() {
        System.out.println(sum(1, 2));
    }

    /**
     * 2469. 温度转换
     * 给你一个四舍五入到两位小数的非负浮点数 celsius 来表示温度，以 摄氏度（Celsius）为单位。
     * 你需要将摄氏度转换为 开氏度（Kelvin）和 华氏度（Fahrenheit），并以数组 ans = [kelvin, fahrenheit] 的形式返回结果。
     * 返回数组 ans 。与实际答案误差不超过 10^5 的会视为正确答案。
     * 注意：
     * 开氏度 = 摄氏度 + 273.15
     * 华氏度 = 摄氏度 * 1.80 + 32.00
     *
     * @param celsius
     * @return
     */
    public double[] convertTemperature(double celsius) {
        double kelvin = celsius + 273.15;
        double fahrenheit = celsius * 1.80 + 32.00;
        double ans[] = new double[]{kelvin, fahrenheit};
        return ans;
    }

    @Test
    public void test2469() {
        double[] doubles = convertTemperature(36.50);
        System.out.println(Arrays.toString(doubles));
    }


    /**
     * 2413. 最小偶倍数
     * 给你一个正整数 n ，返回 2 和 n 的最小公倍数（正整数）。
     * 示例 1：
     * 输入：n = 5
     * 输出：10
     * 解释：5 和 2 的最小公倍数是 10 。
     *
     * @param n
     * @return
     */
    public int smallestEvenMultiple(int n) {
        if (n % 2 == 0) {
            return n;
        } else {
            return n * 2;
        }
    }

    @Test
    public void test2413() {
        System.out.println(smallestEvenMultiple(6));
    }


    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    /**
     * 2236. 判断根结点是否等于子结点之和
     * 给你一个 二叉树 的根结点 root，该二叉树由恰好 3 个结点组成：根结点、左子结点和右子结点。
     * 如果根结点值等于两个子结点值之和，返回 true ，否则返回 false 。
     * 输入：root = [10,4,6]
     * 输出：true
     * 解释：根结点、左子结点和右子结点的值分别是 10 、4 和 6 。
     * 由于 10 等于 4 + 6 ，因此返回 true 。
     *
     * @param root
     * @return
     */
    public boolean checkTree(TreeNode root) {
        if (root.val != root.left.val + root.right.val) {
            return false;
        }
        return true;
    }

    @Test
    public void test2236() {
        System.out.println(checkTree(new TreeNode(10, new TreeNode(5), new TreeNode(6))));
    }

    /**
     * 1486. 数组异或操作
     * 给你两个整数，n 和 start 。
     * 数组 nums 定义为：nums[i] = start + 2*i（下标从 0 开始）且 n == nums.length 。
     * 请返回 nums 中所有元素按位异或（XOR）后得到的结果。
     * 输入：n = 5, start = 0
     * 输出：8
     * 解释：数组 nums 为 [0, 2, 4, 6, 8]，其中 (0 ^ 2 ^ 4 ^ 6 ^ 8) = 8 。
     * "^" 为按位异或 XOR 运算符。
     *
     * @param n
     * @param start
     * @return
     */
    public int xorOperation(int n, int start) {
        /*int[] nums=new int[n];
        for(int i=0;i<n;i++){
            nums[i]=start+2*i;
        }
        int result=0;
        for(int i=0;i<n;i++){
            result^=nums[i];
        }
        return result;*/
        //优化后的代码
        int result = 0;
        for (int i = 0; i < n; i++) {
            result ^= (start + 2 * i);
        }
        return result;
    }

    @Test
    public void test1486() {
        System.out.println(xorOperation(5, 0));
    }

    /**
     * 1512. 好数对的数目
     * 给你一个整数数组 nums
     * 如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ，就可以认为这是一组 好数对 。
     * 返回好数对的数目。
     * 输入：nums = [1,2,3,1,1,3]
     * 输出：4
     * 解释：有 4 组好数对，分别是 (0,3), (0,4), (3,4), (2,5) ，下标从 0 开始
     *
     * @param nums
     * @return
     */
    public int numIdenticalPairs(int[] nums) {
        int number = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] == nums[j]) {
                    number++;
                }
            }
        }
        return number;
    }

    @Test
    public void test1512() {
        System.out.println(numIdenticalPairs(new int[]{1, 2, 3, 1, 1, 3}));
    }

    /**
     * 1534. 统计好三元组
     * 给你一个整数数组 arr ，以及 a、b 、c 三个整数。请你统计其中好三元组的数量。
     * 如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件，则认为它是一个 好三元组 。
     * 0 <= i < j < k < arr.length
     * |arr[i] - arr[j]| <= a
     * |arr[j] - arr[k]| <= b
     * |arr[i] - arr[k]| <= c
     * 其中 |x| 表示 x 的绝对值。
     * 返回 好三元组的数量 。
     * 输入：arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
     * 输出：4
     * 解释：一共有 4 个好三元组：[(3,0,1), (3,0,1), (3,1,1), (0,1,1)] 。
     *
     * @param arr
     * @param a
     * @param b
     * @param c
     * @return
     */
    public int countGoodTriplets(int[] arr, int a, int b, int c) {
        int number = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                for (int k = j + 1; k < arr.length; k++) {
                    if (abs(arr[i] - arr[j]) <= a && abs(arr[j] - arr[k]) <= b && abs(arr[i] - arr[k]) <= c) {
                        number++;
                    }
                }
            }
        }
        return number;
    }

    @Test
    public void test1534() {
        System.out.println(countGoodTriplets(new int[]{3, 0, 1, 1, 9, 7}, 7, 2, 3));
        System.out.println(countGoodTriplets(new int[]{1, 1, 2, 2, 3}, 0, 0, 0));
    }

    /**
     * 584. 寻找用户推荐人
     * 表: Customer
     * +-------------+---------+
     * | Column Name | Type    |
     * +-------------+---------+
     * | id          | int     |
     * | name        | varchar |
     * | referee_id  | int     |
     * +-------------+---------+
     * 在 SQL 中，id 是该表的主键列。
     * 该表的每一行表示一个客户的 id、姓名以及推荐他们的客户的 id。
     * 找出那些 没有被 id = 2 的客户 推荐 的客户的姓名。
     * 以 任意顺序 返回结果表。
     * 结果格式如下所示。
     * 输入：
     * Customer 表:
     * +----+------+------------+
     * | id | name | referee_id |
     * +----+------+------------+
     * | 1  | Will | null       |
     * | 2  | Jane | null       |
     * | 3  | Alex | 2          |
     * | 4  | Bill | null       |
     * | 5  | Zack | 1          |
     * | 6  | Mark | 2          |
     * +----+------+------------+
     * 输出：
     * +------+
     * | name |
     * +------+
     * | Will |
     * | Jane |
     * | Bill |
     * | Zack |
     * +------+
     */
    @Test
    public void test584() {
//        select name from customer where referee_id !=2 or referee_id is null;
    }

    /**
     * 1757. 可回收且低脂的产品
     * 表：Products
     * +-------------+---------+
     * | Column Name | Type    |
     * +-------------+---------+
     * | product_id  | int     |
     * | low_fats    | enum    |
     * | recyclable  | enum    |
     * +-------------+---------+
     * product_id 是该表的主键（具有唯一值的列）。
     * low_fats 是枚举类型，取值为以下两种 ('Y', 'N')，其中 'Y' 表示该产品是低脂产品，'N' 表示不是低脂产品。
     * recyclable 是枚举类型，取值为以下两种 ('Y', 'N')，其中 'Y' 表示该产品可回收，而 'N' 表示不可回收。
     * 编写解决方案找出既是低脂又是可回收的产品编号。
     * 返回结果 无顺序要求 。
     * 返回结果格式如下例所示：
     * 输入：
     * Products 表：
     * +-------------+----------+------------+
     * | product_id  | low_fats | recyclable |
     * +-------------+----------+------------+
     * | 0           | Y        | N          |
     * | 1           | Y        | Y          |
     * | 2           | N        | Y          |
     * | 3           | Y        | Y          |
     * | 4           | N        | N          |
     * +-------------+----------+------------+
     * 输出：
     * +-------------+
     * | product_id  |
     * +-------------+
     * | 1           |
     * | 3           |
     * +-------------+
     */
    @Test
    public void test1757() {
//        select product_id from products where low_fats = 'Y' and recyclable = 'Y';
    }

    /**
     * 709. 转换成小写字母
     * 给你一个字符串 s ，将该字符串中的大写字母转换成相同的小写字母，返回新的字符串。
     * 输入：s = "Hello"
     * 输出："hello"
     *
     * @param s
     * @return
     */
    public String toLowerCase(String s) {
        return s.toLowerCase();
    }

    @Test
    public void test709() {
        System.out.println(toLowerCase("Hello"));
    }

    /**
     * 258. 各位相加（难点）
     * 给定一个非负整数 num，反复将各个位上的数字相加，直到结果为一位数。返回这个结果。
     * 输入: num = 38
     * 输出: 2
     * 解释: 各位相加的过程为：
     * 38 --> 3 + 8 --> 11
     * 11 --> 1 + 1 --> 2
     * 由于 2 是一位数，所以返回 2。
     */
    public int addDigits(int num) {
//        return (num - 1) % 9 + 1; //进阶：时间复杂度为1
        while (num / 10 > 0) { //是否继续相加
            int sum = 0;
            while (num > 0) {
                sum += num % 10; //加个数数
                num /= 10; //去除个位数
            }
            num = sum;
        }
        return num;
    }

    @Test
    public void test258() {
        System.out.println(addDigits(153));
    }

    /**
     * 1281. 整数的各位积和之差
     * 给你一个整数 n，请你帮忙计算并返回该整数「各位数字之积」与「各位数字之和」的差。
     * 输入：n = 234
     * 输出：15
     * 解释：
     * 各位数之积 = 2 * 3 * 4 = 24
     * 各位数之和 = 2 + 3 + 4 = 9
     * 结果 = 24 - 9 = 15
     *
     * @param n
     * @return
     */
    public int subtractProductAndSum(int n) {
        int sum = 0;
        int product = 1; //乘积
        if (n == 1) {
            return 0;
        }
        while (n / 10 > 0) {
            while (n > 0) {
                sum += n % 10;
                product *= n % 10;
                n /= 10;
            }
        }
        return product - sum;
    }

    @Test
    public void test1281() {
        System.out.println(subtractProductAndSum(1));
    }

    /**
     * 231. 2 的幂
     * 给你一个整数 n，请你判断该整数是否是 2 的幂次方。如果是，返回 true ；否则，返回 false 。
     * 如果存在一个整数 x 使得 n == 2x ，则认为 n 是 2 的幂次方。
     * 输入：n = 1
     * 输出：true
     * 解释：20 = 1
     *
     * @param n
     * @return
     */
    public boolean isPowerOfTwo(int n) {
//        for (int i = 0; i < n; i++) {
//            if (pow(2, i) == n) {
//                return true;
//            }
//        }
//        return false;
        // 判断 n 是否大于 0 并且 n 与 (n - 1) 的结果为 0
        //return n > 0 && (n & (n - 1)) == 0; //时间复杂度为O(1)

        if (n <= 0) {
            return false;
        }
        while (n % 2 == 0) {
            n = n / 2;
        }
        return n == 1;
    }

    @Test
    public void test231() {
        System.out.println(isPowerOfTwo(1));
        System.out.println(isPowerOfTwo(6));
        System.out.println(isPowerOfTwo(134217727));//超出时间限制
        System.out.println(6 & 5);
    }

    /**
     * 326. 3 的幂（难点）
     * 给定一个整数，写一个函数来判断它是否是 3 的幂次方。如果是，返回 true ；否则，返回 false 。
     * 整数 n 是 3 的幂次方需满足：存在整数 x 使得 n == 3x
     *
     * @param n
     * @return
     */
    public boolean isPowerOfThree(int n) {
//        for(int i=0;i<n;i++){
//            if(pow(3,i)==n){
//                return true;
//            }
//        }
//        return false; //超出时间限制
        final int maxPowerOfThree = 1162261467; // 3^19 是 32 位整数范围内最大的 3 的幂
        return n > 0 && maxPowerOfThree % n == 0; //进阶：时间复杂度为O(1)

    }

    @Test
    public void test326() {
        System.out.println(isPowerOfThree(27));
        System.out.println(isPowerOfThree(45));
    }

    /**
     * 263. 丑数（难点）
     * 丑数 就是只包含质因数 2、3 和 5 的正整数。
     * 给你一个整数 n ，请你判断 n 是否为 丑数 。如果是，返回 true ；否则，返回 false 。
     * 输入：n = 6
     * 输出：true
     * 解释：6 = 2 × 3
     *
     * @param n
     * @return
     */
    public boolean isUgly(int n) {
        if (n <= 0) {
            return false; // 0 和 负数 不是丑数
        }
        // 去除质因数 2
        while (n % 2 == 0) {
            n /= 2;
        }
        // 去除质因数 3
        while (n % 3 == 0) {
            n /= 3;
        }
        // 去除质因数 5
        while (n % 5 == 0) {
            n /= 5;
        }
        // 如果 n 变为 1，则说明 n 只包含质因数 2、3 和 5
        return n == 1;
    }

    @Test
    public void test263() {
        System.out.println(isUgly(14));
        System.out.println(isUgly(6));
        System.out.println(isUgly(8));
    }

    /**
     * 1470. 重新排列数组
     * 给你一个数组 nums ，数组中有 2n 个元素，按 [x1,x2,...,xn,y1,y2,...,yn] 的格式排列。
     * 请你将数组按 [x1,y1,x2,y2,...,xn,yn] 格式重新排列，返回重排后的数组。
     * 输入：nums = [2,5,1,3,4,7], n = 3
     * 输出：[2,3,5,4,1,7]
     * 解释：由于 x1=2, x2=5, x3=1, y1=3, y2=4, y3=7 ，所以答案为 [2,3,5,4,1,7]
     *
     * @param nums
     * @param n
     * @return
     */
    public int[] shuffle(int[] nums, int n) {
        int[] newArr = new int[nums.length];
        int[] newA = new int[n];
        int[] newB = new int[n];
        for (int i = 0; i < n; i++) {
            newA[i] = nums[i];
            newB[i] = nums[i + n];
        }
        for (int i = 0; i < nums.length; i += 2) {
            newArr[i] = newA[i / 2];
        }
        for (int j = 1; j < nums.length; j += 2) {
            newArr[j] = newB[(j - 1) / 2];
        }
        return newArr;
    }

    @Test
    public void test1470() {
        System.out.println(shuffle(new int[]{2, 5, 1, 3, 4, 7}, 3));
    }

    /**
     * 867. 转置矩阵
     * 给你一个二维整数数组 matrix， 返回 matrix 的 转置矩阵 。
     * 矩阵的 转置 是指将矩阵的主对角线翻转，交换矩阵的行索引与列索引。
     * 输入：matrix = [[1,2,3],[4,5,6],[7,8,9]]
     * 输出：[[1,4,7],[2,5,8],[3,6,9]]
     *
     * @param matrix
     * @return
     */
    public int[][] transpose(int[][] matrix) {
        int[][] newArr = new int[matrix[0].length][matrix.length];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                newArr[j][i] = matrix[i][j];
            }
        }
        return newArr;
    }

    @Test
    public void test867() {
        int[][] transpose = transpose(new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
        for (int[] ints : transpose) {
            System.out.println(Arrays.toString(ints));
        }
    }

    /**
     * 1422. 分割字符串的最大得分
     * 给你一个由若干 0 和 1 组成的字符串 s ，请你计算并返回将该字符串分割成两个 非空 子字符串（即 左 子字符串和 右 子字符串）所能获得的最大得分。
     * 「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。
     * 输入：s = "011101"
     * 输出：5
     * 解释：
     * 将字符串 s 划分为两个非空子字符串的可行方案有：
     * 左子字符串 = "0" 且 右子字符串 = "11101"，得分 = 1 + 4 = 5
     * 左子字符串 = "01" 且 右子字符串 = "1101"，得分 = 1 + 3 = 4
     * 左子字符串 = "011" 且 右子字符串 = "101"，得分 = 1 + 2 = 3
     * 左子字符串 = "0111" 且 右子字符串 = "01"，得分 = 1 + 1 = 2
     * 左子字符串 = "01110" 且 右子字符串 = "1"，得分 = 2 + 1 = 3
     *
     * @param s
     * @return
     */
    public int maxScore(String s) {
        int max = 0;
        int left = 0;
        int right = 0;
        for (int i = 0; i < s.length() - 1; i++) {
            String leftStr = s.substring(0, i + 1);
            String rightStr = s.substring(i + 1);
            for (int j = 0; j < leftStr.length(); j++) {
                if (leftStr.charAt(j) == '0') {
                    left++;
                }
            }
            for (int k = 0; k < rightStr.length(); k++) {
                if (rightStr.charAt(k) == '1') {
                    right++;
                }
            }
            if (right + left > max) {
                max = right + left;
            }
            left = 0;
            right = 0;
        }
        return max;
    }

    @Test
    public void test1422() {
        System.out.println(maxScore("011101"));
    }

    /**
     * 2586. 统计范围内的元音字符串数
     * 给你一个下标从 0 开始的字符串数组 words 和两个整数：left 和 right 。
     * 如果字符串以元音字母开头并以元音字母结尾，那么该字符串就是一个 元音字符串 ，其中元音字母是 'a'、'e'、'i'、'o'、'u' 。
     * 返回 words[i] 是元音字符串的数目，其中 i 在闭区间 [left, right] 内。
     * 输入：words = ["are","amy","u"], left = 0, right = 2
     * 输出：2
     * 解释：
     * - "are" 是一个元音字符串，因为它以 'a' 开头并以 'e' 结尾。
     * - "amy" 不是元音字符串，因为它没有以元音字母结尾。
     * - "u" 是一个元音字符串，因为它以 'u' 开头并以 'u' 结尾。
     * 在上述范围中的元音字符串数目为 2 。
     *
     * @param words
     * @param left
     * @param right
     * @return
     */
    public int vowelStrings(String[] words, int left, int right) {
        int count = 0;
        String arr = "aeiou";
        for (int i = left; i <= right; i++) {
            char first = words[i].charAt(0);
            char last = words[i].charAt(words[i].length() - 1);
//            if (arr.indexOf(first) != -1 && arr.indexOf(last) != -1) {
//                count++;
//            }
            if (arr.contains("" + first) && arr.contains("" + last)) {
                count++;
            }
        }
        return count;
    }

    @Test
    public void test2586() {
        System.out.println(vowelStrings(new String[]{"are", "amy", "u"}, 0, 2));
    }

    /**
     * 852. 山脉数组的峰顶索引
     * 给定一个长度为 n 的整数 山脉 数组 arr ，其中的值递增到一个 峰值元素 然后递减。
     * 返回峰值元素的下标。
     * 你必须设计并实现时间复杂度为 O(log(n)) 的解决方案。
     * <p>
     * 输入：arr = [0,1,0]
     * 输出：1
     *
     * @param arr
     * @return
     */
    public int peakIndexInMountainArray(int[] arr) {
        int max = 0;
        int index = -1;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
                index = i;
            }
        }
        return index;
    }

    @Test
    public void test852() {
        System.out.println(peakIndexInMountainArray(new int[]{0, 1, 0}));
    }


}
